﻿using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Web;
using System.IO;
using System.IO.Compression;

namespace SBS.Common
{
    public class Utility
    {
        public static string GetNowTime()
        {
            return System.DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffK");
        }
        public static DateTime Now()
        {
            return System.DateTime.Now;
        }
        public static string MD5(string txt)
        {
            return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txt, "MD5").ToUpper();
        }
        public static string GetNowTime(string format)
        {
            return System.DateTime.Now.ToString(format);
        }
        public static string GetNowTime(DateTime dt, string format)
        {
            return dt.ToString(format);
        }
        public static string GetNowTime(DateTime dt)
        {
            return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffK");
        }
        public static string GetTimeStamp()
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            DateTime dtNow = DateTime.Parse(DateTime.Now.ToString());
            TimeSpan toNow = dtNow.Subtract(dtStart);
            string timeStamp = toNow.Ticks.ToString();            
            return timeStamp.Substring(0, timeStamp.Length - 7);                        
        }
        public static DateTime GetDateTimeByTimeStamp(string timestamp)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            long lTime = long.Parse(timestamp + "0000000");
            TimeSpan toNow = new TimeSpan(lTime);
            DateTime dtResult = dtStart.Add(toNow);
            return dtResult;
        }
        public static DateTime GetDateTimeByString(string datetime)
        {
            if (datetime.Length == 10 && datetime.IndexOf("-") == 4 && datetime.LastIndexOf("-") == 7)
            {                
                datetime += "T00:00:00.000+08:00";
            }
            if (datetime.Length != 29)
            {
                throw new NullReferenceException("“" + datetime + "”:传入的时间字符串错误，长度必须为29。例如：2010-05-09T08:06:01.123+08:00");
            }
            
            DateTime dt = System.DateTime.ParseExact(datetime, "yyyy-MM-ddTHH:mm:ss.fffK", new System.Globalization.CultureInfo("zh-CN", true));
            return dt;
        }
        public static DateTime GetDateTimeByString(string datetime, string timezone)
        {
            DateTime dt = GetDateTimeByString(datetime);            
            if (timezone.Length == 3 && timezone != "+08")
            {
                int addhour = 0;
                try
                {
                    addhour = -8 + System.Convert.ToInt32(timezone);
                }
                catch (Exception ex)
                {
                    addhour = 0;
                }
                dt = dt.AddHours(addhour);
            }
            return dt;
        }
        public static string GUID()
        {
            return System.Guid.NewGuid().ToString().ToUpper();
        }

        private static StringBuilder ValidateError = new StringBuilder();
        /// <summary>
        /// 验证Xml数据，返回错误信息。如果验证通过，返回空字符串。
        /// </summary>
        /// <param name="XmlFile">Xml文件路径</param>
        /// <param name="XsdFile">Xsd文件路径</param>
        /// <returns></returns>        
        public static string SchemaValidate(string XmlFile, string XsdFile, bool OutputXmlFormat)
        {            
            ValidateError.Remove(0, ValidateError.Length);            
            XmlReader reader = null;
            XmlReaderSettings setting = new XmlReaderSettings();
            
            if (OutputXmlFormat == true)
            {
                setting.ValidationEventHandler += new ValidationEventHandler(Utility.ValidationEventHandlerDetail);
            }
            else
            {
                setting.ValidationEventHandler += new ValidationEventHandler(Utility.ValidationEventHandlerMessage);
            }                
            
            setting.ValidationType = ValidationType.Schema;            
            setting.Schemas.Add(null, XmlReader.Create(XsdFile));            
            reader = XmlReader.Create(XmlFile, setting);            
            try
            {
                while (reader.Read())
                { }
                reader.Close();
            }
            catch (XmlException e)
            {                
                reader.Close();
            }
            string html = "";
            if (OutputXmlFormat == true)
            {
                html = "<?xml version=\"1.0\" encoding=\"utf-8\"?><root>" + ValidateError.ToString() + "</root>";
            }
            else
            {
                html = ValidateError.ToString();                
            }
            ValidateError.Remove(0, ValidateError.Length);
            return html;
        }
        public static string SchemaValidate(XmlDocument dom, string XsdFile, bool OutputXmlFormat)
        {                        
            ValidateError.Remove(0, ValidateError.Length);
            XmlReader xsdreader = XmlReader.Create(XsdFile);
            XmlReader reader = null;
            
            XmlReaderSettings setting = new XmlReaderSettings();
            if (OutputXmlFormat == true)
            {
                setting.ValidationEventHandler += new ValidationEventHandler(Utility.ValidationEventHandlerDetail);
            }
            else
            {
                setting.ValidationEventHandler += new ValidationEventHandler(Utility.ValidationEventHandlerMessage);
            }
            setting.ValidationType = ValidationType.Schema;
            setting.Schemas.Add(null, xsdreader);
            XmlNodeReader nodeReader = new XmlNodeReader(dom);            
            reader = XmlReader.Create(nodeReader, setting);
            try
            {
                while (reader.Read())
                { }
                reader.Close();
                xsdreader.Close();
            }
            catch (XmlException e)
            {
                reader.Close();
                xsdreader.Close();
            }
            nodeReader.Close();

            string html = "";
            if (OutputXmlFormat == true)
            {
                html = "<?xml version=\"1.0\" encoding=\"utf-8\"?><root>" + ValidateError.ToString() + "</root>";
            }
            else
            {
                html = ValidateError.ToString();
            }
            ValidateError.Remove(0, ValidateError.Length);
            return html;
        }
        public static string SchemaValidate(string XmlFile, string XsdFile)
        {
            return SchemaValidate(XmlFile, XsdFile, false);
        }
        public static string SchemaValidate(XmlDocument dom, string XsdFile)
        {
            return SchemaValidate(dom, XsdFile, false);
        }
        public static XmlDocument SchemaValidateOutputXml(string XmlFile, string XsdFile)
        {
            string xmlstr = SchemaValidate(XmlFile, XsdFile, true);
            XmlDocument dom = new XmlDocument();
            dom.LoadXml(xmlstr);
            return dom;
        }
        public static XmlDocument SchemaValidateOutputXml(XmlDocument dom, string XsdFile)
        {
            string xmlstr = SchemaValidate(dom, XsdFile, true);
            XmlDocument xmldom = new XmlDocument();
            xmldom.LoadXml(xmlstr);
            return xmldom;
        }
        private static void ValidationEventHandlerDetail(object sender, ValidationEventArgs args)
        {
            ValidateError.Append("<validateinfo>");
            ValidateError.Append("<severity>" + args.Severity + "</severity>");
            ValidateError.Append("<message>" + args.Message.Replace("<","&lt;").Replace(">","&gt;") + "</message>");
            ValidateError.Append("<line>" + args.Exception.LineNumber + "</line>");
            ValidateError.Append("<position>" + args.Exception.LinePosition + "</position>");
            ValidateError.Append("<source>" + args.Exception.SourceUri+"</source>");
            ValidateError.Append("</validateinfo>");
        }
        private static void ValidationEventHandlerMessage(object sender, ValidationEventArgs args)
        {
            ValidateError.Append("args.Message");            
        }

        public static XmlDocument GetLangDictionary(string lang)
        {
            if (string.IsNullOrEmpty(lang))
            {
                lang = "zh-cn";
            }
            string xmlfile = SBS.Base.GlobalCache.GetSiteConfig().SelectSingleNode("/root/xmldata").InnerText + "language\\" + lang + ".xml";

            XmlNode LangNode = SBS.Base.GlobalCache.GetSiteConfig().SelectSingleNode("/root/language/lang[@name='" + lang + "']");
            if (LangNode == null)
            {
                XmlDocument empty = new XmlDocument();
                empty.LoadXml("<root/>");
                return empty;
            }
            

            if (!File.Exists(xmlfile))
            {
                XmlDocument empty = new XmlDocument();
                empty.LoadXml("<root/>");
                return empty;
                    
            }
            if (HttpRuntime.Cache[xmlfile] != null)
            {
                return (XmlDocument)HttpRuntime.Cache[xmlfile];
            }

            XmlDocument dom = new XmlDocument();
            dom.Load(xmlfile);

            System.Web.Caching.CacheDependency dep = new System.Web.Caching.CacheDependency(xmlfile);
            HttpRuntime.Cache.Insert(xmlfile, dom, dep, System.DateTime.Now.AddHours(12), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, new System.Web.Caching.CacheItemRemovedCallback(SBS.Base.GlobalCache.CacheRemovedCallBack));
            return (XmlDocument)HttpRuntime.Cache[xmlfile];
        }

        /// <summary>
        /// 分析路径，由左至右（由浅至深）递归创建目录，仅接受绝对路径。
        /// </summary>
        /// <param name="DirFullPath">完整的路径，如 c:\temp\abc</param>
        public static bool CreateDirectoryEx(string DirFullPath)
        {
            if (Directory.Exists(DirFullPath)) return false;
            if (DirFullPath.Contains(':')==false) return false;
            if (DirFullPath.Contains('\\') == false)
            {
                if (DirFullPath.Contains("/") == false) return false;
            }
            string[] DirArr = DirFullPath.Split(new char[]{'/','\\'});
            //StringBuilder Path = new StringBuilder(128);

            for (int i = 0; i < DirArr.Length; i++ )
            {
                if (string.IsNullOrEmpty(DirArr[i])) continue;
                string path = string.Join(@"\", DirArr, 0, i + 1);
                if (Directory.Exists(path)) continue;
                if (path.EndsWith(":")) continue;
                Directory.CreateDirectory(path);                
            }
            return true;
            //下面需要验证，曾出错。
            //if (!DirFullPath.Contains('\\') || !DirFullPath.Contains(':')) return -1;
            //int Counter = 0;
            //string[] DirArr = DirFullPath.Split(new char[] { '/', '\\' });
            //for (int i = 0; i < DirArr.Length; i++)
            //{
            //    if (string.IsNullOrEmpty(DirArr[i])) continue;
            //    string path = string.Join(@"\", DirArr, 0, i + 1);
            //    if (Directory.Exists(path)) continue;
            //    Directory.CreateDirectory(path);
            //    Counter++;
            //}
            //return Counter;
        }

        public static void CompressContextOutput(HttpContext Context)
        {
            #region 压缩输出
            string acceptEncoding = Context.Request.Headers["Accept-Encoding"];
            if (!String.IsNullOrEmpty(acceptEncoding))
            {
                if (acceptEncoding.Contains("gzip"))
                {
                    Context.Response.AppendHeader("Content-encoding", "gzip");
                    Context.Response.Filter = new GZipStream(Context.Response.Filter, CompressionMode.Compress);
                }
                else if (acceptEncoding.Contains("deflate"))
                {
                    Context.Response.AppendHeader("Content-encoding", "deflate");
                    Context.Response.Filter = new DeflateStream(Context.Response.Filter, CompressionMode.Compress);
                }                
            }
            #endregion
        }
        public static void CompressContextOutput()
        {
            CompressContextOutput(HttpContext.Current);
        }

        /// <summary>
        /// 复制文件夹到指定的位置。SourceDir 下面所有的子目录、文件都将被复制到 TargetDir 位置下。
        /// </summary>
        /// <param name="SourceDir">源文件夹</param>
        /// <param name="TargetDir">目标位置</param>
        /// <param name="OverWrite">是否覆盖已存在的文件</param>
        public static void CopyDirecotry(string SourceDir, string TargetDir, bool OverWrite)
        {
            if (!SourceDir.EndsWith(@"\"))
            {
                SourceDir += @"\";
            }
            if (!TargetDir.EndsWith(@"\"))
            {
                TargetDir += @"\";
            }
            SBS.Common.Utility.CreateDirectoryEx(TargetDir);
            DirectoryInfo Src = new DirectoryInfo(SourceDir);
            FileInfo[] fs = Src.GetFiles("*", SearchOption.AllDirectories);
            int SrcLen = SourceDir.Length;

            foreach (FileInfo f in fs)
            {
                string SrcFile = f.FullName;
                string TarFile = TargetDir + f.FullName.Substring(SrcLen);
                string Folder = TarFile.Substring(0, TarFile.LastIndexOf(@"\"));
                SBS.Common.Utility.CreateDirectoryEx(Folder);
                File.Copy(SrcFile, TarFile, OverWrite);
            }
        }

        /// <summary>
        /// 把Url字符串中的查询部分转换为字典。该方法有反向方法：DictionaryToQueryString
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public static Dictionary<string, string> QueryStringToDictionary(string Url)
        {
            if (string.IsNullOrEmpty(Url)) return null;

            if (Url.StartsWith("/"))
            {
                Url = Url.Substring(1);
            }
            if (Url.StartsWith("?"))
            {
                Url = Url.Substring(1);
            }
            if (Url.Contains("?"))
            {
                Url = Url.Substring(Url.IndexOf("?") + 1);
            }

            Dictionary<string, string> Arr = new Dictionary<string, string>();

            if (Url.Contains("&") == false)
            {
                if (Url.Contains("="))
                {
                    Arr.Add(Url.Substring(0, Url.IndexOf("=")), Url.Substring(Url.IndexOf("=") + 1));
                }
                else
                {
                    Arr.Add(Url, null);
                }
                return Arr;
            }

            string[] KeyValue = Url.Split('&');
            foreach (string Key in KeyValue)
            {
                if (Key.Contains("="))
                {
                    Arr.Add(Key.Substring(0, Key.IndexOf("=")), Key.Substring(Key.IndexOf("=") + 1));
                }
                else
                {
                    Arr.Add(Key, null);
                }
            }
            return Arr;
        }

        /// <summary>
        /// 把字符串字典用 & 符合连接为 url 形式的 QueryString。该方法有反向方法：QueryStringToDictionary
        /// </summary>
        /// <param name="Dict"></param>
        /// <returns></returns>
        public static string DictionaryToQueryString(Dictionary<string, string> Dict)
        {
            List<string> Arr = new List<string>();
            Dict.Each(n =>Arr.Add("{0}={1}".FormatString(n.Key, n.Value)));
            return Arr.ToArray().Join("&");
        }

        /// <summary>
        /// 从指定文件中读取所有行到 List，内部使用 FileStream
        /// </summary>
        /// <param name="FileFullName"></param>
        /// <param name="Line">指定行内容</param>
        /// <param name="Comparison">StringComparison</param>
        /// <returns></returns>
        public static bool TxtFileExistsLine(string FileFullName, string Line, StringComparison Comparison)
        {
            if (!File.Exists(FileFullName)) return false;
            bool status = false;
            FileStream Fs = new FileStream(FileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            StreamReader Reader = new StreamReader(Fs);
            while (!Reader.EndOfStream)
            {
                if (Line.Equals(Reader.ReadLine(), Comparison))
                {
                    status = true;
                    break;
                }                
            }
            Reader.Close();
            Fs.Close();
            return status;
        }
        /// <summary>
        /// 从指定文件中读取所有行到 List，内部使用 FileStream
        /// </summary>
        /// <param name="FileFullName"></param>
        /// <param name="Line">指定行内容</param>        
        /// <returns></returns>
        public static bool TxtFileExistsLine(string FileFullName, string Line)
        {
            if (!File.Exists(FileFullName)) return false;
            bool status = false;
            FileStream Fs = new FileStream(FileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            StreamReader Reader = new StreamReader(Fs);
            while (!Reader.EndOfStream)
            {
                if (Line.Equals(Reader.ReadLine()))
                {
                    status = true;
                    break;
                }
            }
            Reader.Close();
            Fs.Close();
            return status;
        }
        /// <summary>
        /// 从指定文件中读取所有行到 List，内部使用 FileStream
        /// </summary>
        /// <param name="FileFullName"></param>
        /// <returns>如果文件不存在，返回 null</returns>
        public static List<string> TxtFileReadLines(string FileFullName)
        {
            if (!File.Exists(FileFullName)) return null;
            List<string> Arr = new List<string>();
            FileStream Fs = new FileStream(FileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            StreamReader Reader = new StreamReader(Fs);
            while (!Reader.EndOfStream)
            {
                Arr.Add(Reader.ReadLine());
            }
            Reader.Close();
            Fs.Close();
            return Arr;
        }
        /// <summary>
        /// 删除文件指定行。内部使用 FileStream 读写锁。该方法不改变原文件的编码。
        /// </summary>
        /// <param name="FileFullName"></param>
        /// <param name="Line">指定行的内容</param>
        /// <returns>如果文件不存在或没有发现指定行，返回 false，发现并删除，返回true</returns>
        public static bool TxtFileRemoveLine(string FileFullName, string Line)
        {
            if (!File.Exists(FileFullName)) return false;
            List<string> Arr = TxtFileReadLines(FileFullName);
            bool Status = false;
            foreach (string id in Arr)
            {
                if (id.Equals(Line))
                {
                    Status = true;
                    break;
                }
            }
            if (Status == false) return false;
            Encoding Enc = GetEncoding(FileFullName);
            FileStream Fs = new FileStream(FileFullName, FileMode.Create, FileAccess.Write, FileShare.Read);
            StreamWriter Writer = new StreamWriter(Fs, Enc);
            foreach (string id in Arr)
            {
                if (id.Equals(Line)) continue;
                Writer.WriteLine(id);
            }
            Writer.Close();
            Fs.Close();
            return true;
        }

        /// <summary>
        /// 续加新行到文件结尾处。如果文件不存在，则新创建。
        /// </summary>
        /// <param name="FileFullName"></param>
        /// <param name="Line"></param>
        /// <param name="ExitIfLineExists">默认为 false。如果传入的 Line 已经存在于文件中，ExitIfLineExists 为 true 时，不再添加 Line 到文件，否则重复添加 Line。</param>
        /// <returns>添加成功返回 true， 否则为 false。</returns>
        public static bool TxtFileAppendLine(string FileFullName, string Line, bool ExitIfLineExists)
        {            
            if (ExitIfLineExists==true && TxtFileExistsLine(FileFullName, Line)==true)
            {
                return false;
            }            
            FileStream Fs = new FileStream(FileFullName, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter Writer = new StreamWriter(Fs);
            Writer.WriteLine(Line);
            Writer.Close();
            Fs.Close();
            return true;
        }
        /// <summary>
        /// 续加新行到文件结尾处。如果文件不存在，则新创建。
        /// </summary>
        /// <param name="FileFullName"></param>
        /// <param name="Line"></param>
        /// <returns></returns>
        public static bool TxtFileAppendLine(string FileFullName, string Line)
        {
            return TxtFileAppendLine(FileFullName, Line, false);
        }

        /// <summary>
        /// 获取指定文件的编码
        /// </summary>
        /// <param name="FileFullName"></param>
        /// <returns></returns>
        public static System.Text.Encoding GetEncoding(string FileFullName)
        {
            FileStream fs = new FileStream(FileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            System.Text.Encoding Encoding = GetEncoding(fs);
            fs.Close();
            return Encoding;
        }
        /// <summary>
        /// 获取 Stream 的编码
        /// </summary>
        /// <param name="fs"></param>
        /// <returns></returns>
        public static System.Text.Encoding GetEncoding(Stream Stream)
        {
            /*byte[] Unicode=new byte[]{0xFF,0xFE};  
            byte[] UnicodeBIG=new byte[]{0xFE,0xFF};  
            byte[] UTF8=new byte[]{0xEF,0xBB,0xBF};*/

            BinaryReader r = new BinaryReader(Stream, System.Text.Encoding.Default);
            byte[] ss = r.ReadBytes(3);
            r.Close();
            //编码类型 Coding=编码类型.ASCII;   
            if (ss[0] >= 0xEF)
            {
                if (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF)
                {
                    return System.Text.Encoding.UTF8;
                }
                else if (ss[0] == 0xFE && ss[1] == 0xFF)
                {
                    return System.Text.Encoding.BigEndianUnicode;
                }
                else if (ss[0] == 0xFF && ss[1] == 0xFE)
                {
                    return System.Text.Encoding.Unicode;
                }
                else
                {
                    return System.Text.Encoding.Default;
                }
            }
            else
            {
                return System.Text.Encoding.Default;
            }
        } 
    }
}
